
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 *版权所有2002-2018原作者。
 *
 *根据Apache许可证2.0版（“许可证”）获得许可；
 *除非符合许可证，否则不得使用此文件。
 *您可以在以下地址获取许可证副本：
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 *除非适用法律要求或书面同意，软件
 *根据许可证分发的内容是按“原样”分发的，
 *无任何明示或暗示的保证或条件。
 *有关管理权限和
 *许可证下的限制。
 */

package org.springframework.beans.factory.config;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.lang.Nullable;

/**
 * {@link FactoryBean} which returns a value which is the result of a static or instance
 * method invocation. For most use cases it is better to just use the container's
 * built-in factory method support for the same purpose, since that is smarter at
 * converting arguments. This factory bean is still useful though when you need to
 * call a method which doesn't return any value (for example, a static class method
 * to force some sort of initialization to happen). This use case is not supported
 * by factory methods, since a return value is needed to obtain the bean instance.
 *
 * <p>Note that as it is expected to be used mostly for accessing factory methods,
 * this factory by default operates in a <b>singleton</b> fashion. The first request
 * to {@link #getObject} by the owning bean factory will cause a method invocation,
 * whose return value will be cached for subsequent requests. An internal
 * {@link #setSingleton singleton} property may be set to "false", to cause this
 * factory to invoke the target method each time it is asked for an object.
 *
 * <p><b>NOTE: If your target method does not produce a result to expose, consider
 * {@link MethodInvokingBean} instead, which avoids the type determination and
 * lifecycle limitations that this {@link MethodInvokingFactoryBean} comes with.</b>
 *
 * <p>This invoker supports any kind of target method. A static method may be specified
 * by setting the {@link #setTargetMethod targetMethod} property to a String representing
 * the static method name, with {@link #setTargetClass targetClass} specifying the Class
 * that the static method is defined on. Alternatively, a target instance method may be
 * specified, by setting the {@link #setTargetObject targetObject} property as the target
 * object, and the {@link #setTargetMethod targetMethod} property as the name of the
 * method to call on that target object. Arguments for the method invocation may be
 * specified by setting the {@link #setArguments arguments} property.
 *
 * <p>This class depends on {@link #afterPropertiesSet()} being called once
 * all properties have been set, as per the InitializingBean contract.
 *
 * <p>An example (in an XML based bean factory definition) of a bean definition
 * which uses this class to call a static factory method:
 *
 * <pre class="code">
 * &lt;bean id="myObject" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"&gt;
 *   &lt;property name="staticMethod" value="com.whatever.MyClassFactory.getInstance"/&gt;
 * &lt;/bean&gt;</pre>
 *
 * <p>An example of calling a static method then an instance method to get at a
 * Java system property. Somewhat verbose, but it works.
 *
 * <pre class="code">
 * &lt;bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"&gt;
 *   &lt;property name="targetClass" value="java.lang.System"/&gt;
 *   &lt;property name="targetMethod" value="getProperties"/&gt;
 * &lt;/bean&gt;
 *
 * &lt;bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"&gt;
 *   &lt;property name="targetObject" ref="sysProps"/&gt;
 *   &lt;property name="targetMethod" value="getProperty"/&gt;
 *   &lt;property name="arguments" value="java.version"/&gt;
 * &lt;/bean&gt;</pre>
 *
 * @author Colin Sampaleanu
 * @author Juergen Hoeller
 * @since 21.11.2003
 * @see MethodInvokingBean
 * @see org.springframework.util.MethodInvoker
 */
/**
 *｛@link FactoryBean｝，它返回一个值，该值是静态或实例的结果
 *方法调用。对于大多数用例，最好只使用容器的
 *内置的工厂方法支持相同的目的，因为这在
 *转换参数。这个工厂bean在需要时仍然有用
 *调用不返回任何值的方法（例如，静态类方法
 *以强制进行某种初始化）。不支持此用例
 *通过工厂方法，因为需要返回值来获取bean实例。
 *
 *＜p＞注意，由于它预计主要用于访问工厂方法，
 *默认情况下，该工厂以<b>singleton</b>方式运行。第一个请求
 *拥有bean工厂对｛@link#getObject｝的调用将导致方法调用，
 *其返回值将被缓存以用于后续请求。内部
 *｛@link#setSingleton singleton｝属性可能设置为“false”，从而导致
 *工厂在每次请求对象时调用目标方法。
 *
 *＜p＞＜b＞注意：如果目标方法没有生成要公开的结果，请考虑
 *｛@link MethodInvokingBean｝，这避免了类型确定和
 *此｛@link MethodInvokingFactoryBean｝附带的生命周期限制</b>
 *
 *＜p＞此调用程序支持任何类型的目标方法。可以指定静态方法
 *通过将｛@link#setTargetMethod targetMethod｝属性设置为表示
 *静态方法名，｛@link#setTargetClass targetClass｝指定Class
 *静态方法是在其上定义的。或者，目标实例方法可以是
 *通过将｛@link#setTargetObject targetObject｝属性设置为目标
 *对象，以及｛@link#setTargetMethod targetMethod｝属性作为
 *方法调用该目标对象。方法调用的参数可以是
 *通过设置｛@link#setArguments｝属性指定。
 *
 *＜p＞这个类依赖于｛@link#afterPropertiesSet（）｝被调用一次
 *根据InitializingBean合同，所有财产都已设置。
 *
 *＜p＞bean定义的示例（在基于XML的bean工厂定义中）
 *它使用该类调用静态工厂方法：
 *
 *<pre-class=“code”>
 *&lt；bean id=“myObject”class=“org.springframework.beans.factory.config.MethodInvokingFactoryBean”&gt；
 *&lt；property name=“staticMethod”value=“com.whatever.MyClassFactory.getInstance”/&gt；
 *&lt/豆&gt</前>
 *
 *＜p＞调用静态方法然后调用实例方法以获取
 *Java系统属性。有点啰嗦，但它有效。
 *
 *<pre-class=“code”>
 *&lt；bean id=“sysProps”class=“org.springframework.beans.factory.config.MethodInvokingFactoryBean”&gt；
 *&lt；property name=“targetClass”value=“java.lang.System”/&gt；
 *&lt；property name=“targetMethod”value=“getProperties”/&gt；
 *&lt/豆&gt；
 *
 *&lt；bean id=“javaVersion”class=“org.springframework.beans.factory.config.MethodInvokingFactoryBean”&gt；
 *&lt；property name=“targetObject”ref=“sysProps”/&gt；
 *&lt；property name=“targetMethod”value=“getProperty”/&gt；
 *&lt；property name=“arguments”value=“java.version”/&gt；
 *&lt/豆&gt</前>
 *
 *@作者Colin Sampaleanu
 *@作者Juergen Hoeller
 *@自2003年11月21日
 *@参见MethodInvokingBean
 *@参见org.springframework.util.MethodInvoker
 */
public class MethodInvokingFactoryBean extends MethodInvokingBean implements FactoryBean<Object> {

	private boolean singleton = true;

	private boolean initialized = false;

	/** Method call result in the singleton case. */
	@Nullable
	private Object singletonObject;


	/**
	 * Set if a singleton should be created, or a new object on each
	 * {@link #getObject()} request otherwise. Default is "true".
	 */
	/** Method call result in the singleton case. */
	@可为空
	私有对象singletonObject；


	/**
	 *设置是否应创建一个单例，或在每个单例上创建一个新对象
	 *否则｛@link#getObject（）｝请求。默认值为“true”。
	 */
	public void setSingleton(boolean singleton) {
		this.singleton = singleton;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		prepare();
		if (this.singleton) {
			this.initialized = true;
			this.singletonObject = invokeWithTargetException();
		}
	}


	/**
	 * Returns the same value each time if the singleton property is set
	 * to "true", otherwise returns the value returned from invoking the
	 * specified method on the fly.
	 */
	/**
	 *如果设置了singleton属性，则每次返回相同的值
	 *设置为“true”，否则返回调用
	 *指定的方法。
	 */
	@Override
	@Nullable
	public Object getObject() throws Exception {
		if (this.singleton) {
			if (!this.initialized) {
				throw new FactoryBeanNotInitializedException();
			}
			// Singleton: return shared object.
			// Singleton：返回共享对象。
			return this.singletonObject;
		}
		else {
			// Prototype: new object on each call.
			// 原型：每次调用都有新对象。
			return invokeWithTargetException();
		}
	}

	/**
	 * Return the type of object that this FactoryBean creates,
	 * or {@code null} if not known in advance.
	 */
	/**
	 *返回此FactoryBean创建的对象类型，
	 *或｛@code null｝（如果事先不知道）。
	 */
	@Override
	public Class<?> getObjectType() {
		if (!isPrepared()) {
			// Not fully initialized yet -> return null to indicate "not known yet".
			// 尚未完全初始化->返回null表示“未知”。
			return null;
		}
		return getPreparedMethod().getReturnType();
	}

	@Override
	public boolean isSingleton() {
		return this.singleton;
	}

}
